구성환경
SpringBoot, Gradle, Thymeleaf, Jpa(JPQL), Jar, MariaDB
지난번 스프링 부트 프로젝트를 생성하였고, 이번 포스팅에서는 DBMS중 하나인 MariaDB에 스키마 생성 및 연동하는 부분을 진행한다.
MariaDB 설치가 안되어있는 경우 링크를 통해 설치 후 진행한다.
프로젝트내 static/database/script.sql이 있으니 테이블 생성 시 참고한다. script.sql 다운로드
데이터베이스 생성하기
CREATE DATABASE IF NOT EXISTS `board_study`
테이블 생성하기
CREATE TABLE IF NOT EXISTS `board` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'PK', `title` varchar(200) NOT NULL COMMENT '제목', `content` text NOT NULL COMMENT '내용', `read_cnt` int(11) NOT NULL DEFAULT 0 COMMENT '조회수', `register_id` VARCHAR(100) NOT NULL COMMENT '작성자', `register_time` DATETIME NULL DEFAULT NULL COMMENT '작성일', `update_time` DATETIME NULL DEFAULT NULL COMMENT '수정일', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='게시판';
Dependency 추가프로젝트로 돌아와 JPA(JPA란?)를 사용하기 위한 Dependency 추가와 데이터베이스 설정을 한다.build.gradleimplementation 'org.springframework.boot:spring-boot-starter-data-jpa'Gradle 적용이클립스프로젝트 우 클릭 → Gradle → Refresh Gradle Project인텔리제이build.gradle에서 Load Gradle Changes (Ctrl+Shift+O)또는 우측 Gradle 탭에서 Reload All Gradle Projects
데이터베이스 설정(application.properties)spring.datasource.driverClassName=org.mariadb.jdbc.Driverspring.datasource.url=jdbc:mariadb://localhost:3306/데이터베이스명spring.datasource.username=계정spring.datasource.password=비밀번호
lombok(https://projectlombok.org/download) 설치lombok (Getter, Setter 등의 반복 메서드를 자동으로 연결하고 도구를 빌드하여 Java를 향상하는 라이브러리)lombok.jar를 다운로드 받은 후 실행한다.① Specify location..를 통해 이클립스가 설치된 경로를 선택한다.② install / update③ Quit installer
설치가 완료되면 이클립스 설치 경로에 lombok.jar가 생성되고 eclipse.ini에 설정이 추가된다.*STS의 경우 SpringToolSuite4.ini이제 이클립스를 재시작한다.재시작 후에 이클립스 상단 탭 Project -> Clean -> 해당 프로젝트 Clean
이제 JPA를 사용하기 위한 설정이 끝났으니, 서비스를 구현해본다.Service까지 구현하는 패키지 구조는 아래와 같다.
이름설명BoardRequestDto.java게시판 요청 데이터를 담당BoardResponseDto.java게시판 응답 데이터를 담당BaseTimeEntity.java반복되는 날짜 데이터의 공통처리를 담당Board.javaboard 테이블의 @EntityBoardRepository.javaJpaRepository 구현체BoardService.java게시판 @Service
BoardRequestDto.java게시물 등록, 게시물 수정, 게시물 상세 조회에 필요한 필드를 정의한다.toEntity() 메서드는 Board Entity를 builder 하여 사용한다.
package com.board.study.dto.board;import com.board.study.entity.board.Board;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.Setter;@Getter@Setter@NoArgsConstructorpublic class BoardRequestDto {private Long id;private String title;private String content;private String registerId;public Board toEntity() {return Board.builder().title(title).content(content).registerId(registerId).build();}}
BoardResponseDto.java게시물 목록, 게시물 상세 조회에 필요한 필드를 정의한다.Board Entity를 BoardResponseDto에 맞게 변환하는 생성자를 생성한다.
package com.board.study.dto.board;import com.board.study.entity.board.Board;import java.time.LocalDateTime;import lombok.Getter;@Getterpublic class BoardResponseDto {private Long id;private String title;private String content;private int readCnt;private String registerId;private LocalDateTime registerTime;public BoardResponseDto(Board entity) {this.id = entity.getId();this.title = entity.getTitle();this.content = entity.getContent();this.readCnt = entity.getReadCnt();this.registerId = entity.getRegisterId();this.registerTime = entity.getRegisterTime();}@Overridepublic String toString() {return "BoardListDto [id=" + id + ", title=" + title + ", content=" + content + ", readCnt=" + readCnt +", registerId=" + registerId + ", registerTime=" + registerTime + "]";}}
BaseTimeEntity.javaEntity에서 공통적으로 사용될 날짜 필드를 관리할 클래스를 정의한다.꼭 날짜가 아니더라도 공통적으로 반복되는 필드를 정의하여 사용해도 된다.
package com.board.study.entity;import java.time.LocalDateTime;import javax.persistence.EntityListeners;import javax.persistence.MappedSuperclass;import org.springframework.data.annotation.CreatedDate;import org.springframework.data.annotation.LastModifiedDate;import org.springframework.data.jpa.domain.support.AuditingEntityListener;import lombok.Getter;@Getter@MappedSuperclass@EntityListeners(AuditingEntityListener.class)public class BaseTimeEntity {@CreatedDateprivate LocalDateTime registerTime;@LastModifiedDateprivate LocalDateTime updateTime;}
Board.javaEntity를 정의한다.테이블의 모든 필드와 Builder 생성자를 구현한다.혹시나 테이블명이 Class명과 다를 경우 @Entity(name = "테이블명") 을 설정한다.
package com.board.study.entity.board;import com.board.study.entity.BaseTimeEntity;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import lombok.AccessLevel;import lombok.Builder;import lombok.Getter;import lombok.NoArgsConstructor;@NoArgsConstructor(access = AccessLevel.PROTECTED)@Getter@Entitypublic class Board extends BaseTimeEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String title;private String content;private int readCnt;private String registerId;@Builderpublic Board(Long id, String title, String content, int readCnt, String registerId) {this.id = id;this.title = title;this.content = content;this.readCnt = readCnt;this.registerId = registerId;}}
BoardRepository.javaJpaRepository를 상속받아 CRUD의 기능을 담당하는 인터페이스를 생성한다.그리고 @Query을 사용한 JPQL 방식의 updateBoard() 메서드도 구현해본다. 이 방식으로 쿼리를 직접 작성하여 사용할 수도 있다.
package com.board.study.entity.board;import com.board.study.dto.board.BoardRequestDto;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.Modifying;import org.springframework.data.jpa.repository.Query;import org.springframework.data.repository.query.Param;import org.springframework.transaction.annotation.Transactional;public interface BoardRepository extends JpaRepository < Board, Long > {String UPDATE_BOARD = "UPDATE Board " + "SET TITLE = :#{#boardRequestDto.title}, " + "CONTENT = :#{#boardRequestDto.content}, " + "UPDATE_TIME = NOW() " + "WHERE ID = :#{#boardRequestDto.id}";@Transactional@Modifying@Query(value = UPDATE_BOARD, nativeQuery = true)public int updateBoard(@Param("boardRequestDto") BoardRequestDto boardRequestDto);}
BoardService.java게시판 기능을 담당할 Service 클래스로 나중에 파일 및 페이징 처리를 추가할 예정이다.지금은 간단한 CRUD 메서드만 작성한다.
package com.board.study.service;import java.util.List;import java.util.stream.Collectors;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import com.board.study.dto.board.BoardResponseDto;import com.board.study.dto.board.BoardRequestDto;import com.board.study.entity.board.BoardRepository;import lombok.RequiredArgsConstructor;@RequiredArgsConstructor@Servicepublic class BoardService {private final BoardRepository boardRepository;@Transactionalpublic Long save(BoardRequestDto boardSaveDto) {return boardRepository.save(boardSaveDto.toEntity()).getId();}@Transactional(readOnly = true)public List < BoardResponseDto > findAll() {return boardRepository.findAll().stream().map(BoardResponseDto::new).collect(Collectors.toList());}public BoardResponseDto findById(Long id) {return new BoardResponseDto(boardRepository.findById(id).get());}public int updateBoard(BoardRequestDto boardRequestDto) {return boardRepository.updateBoard(boardRequestDto);}public void deleteById(Long id) {boardRepository.deleteById(id);}}
BoardStudyApplicationApplication 클래스에 @EnableJpaAuditing을 추가해 Auditing 기능을 활성화한다.package com.board.study;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.data.jpa.repository.config.EnableJpaAuditing;@EnableJpaAuditing // JPA Auditing@SpringBootApplicationpublic class BoardStudyApplication {public static void main(String[] args) {SpringApplication.run(BoardStudyApplication.class, args);}}
어노테이션에 대한 설명이름설명@NoArgsConstructor파라미터가 없는 생성자를 생성한다.@AllArgsConstructor모든 인자를 가진 생성자를 생성한다.@RequiredArgsConstructor초기화 되지 않은 모든 final 필드, @NonNull로 마크돼있는 모든 필드들에 대한 생성자를 자동으로 생성해준다.@GetterClass 내 모든 필드의 Getter method를 자동 생성한다.@SetterClass 내 모든 필드의 Setter method를 자동 생성한다.@Entity실제 DB의 테이블과 매칭될 Class임을 명시한다.즉, 테이블과 링크될 클래스임을 나타낸다.@MappedSuperclass이 클래스를 상속하는 엔티티에 매핑되는 테이블에 생성한다.@EntityListeners(AuditingEntityListener.class)JPA 내부에서 엔티티 객체가 생성/변경되는 것을 감지하는 역할을 한다.@CreateDateJPA에서 엔티티의 생성 시간을 처리한다.@LastModifiedDate최종 수정 시간을 자동으로 처리한다.@Id해당 테이블의 PK 필드를 나타낸다.@GeneratedValue(strategy = GenerationType.IDENTITY)PK의 생성 규칙을 나타낸다.@Builder어느 필드에 어떤 값을 채워야 할지 명확하게 정하여 생성 시점에 값을 채워준다.@Transactional선언적 트랜잭션을 사용한다.@Modifying@Query Annotation으로 작성 된 변경, 삭제 쿼리를 사용할때 사용한다.@QuerySQL을 JPQL로 작성할 수 있고, nativeQuery=true 옵션으로 네이티브 쿼리도 사용 가능하게 한다.
테스트 코드 작성화면을 구성하기 전, 기능들이 정상적으로 작동하는지 확인하기 위해 JUnit(테스트 프레임워크)를 이용해 테스트를 진행한다.프로젝트 생성 당시 설명했었던 것처럼 src/test/java에 Test Class가 생성되는데, 추가로 생성해서 진행해도 되지만 여기서는 생성되어있는 BoardStudyApplicationTests.java를 사용한다.
package com.board.study;import java.util.List;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import com.board.study.dto.board.BoardResponseDto;import com.board.study.dto.board.BoardRequestDto;import com.board.study.service.BoardService;@SpringBootTestclass BoardStudyApplicationTests {@Autowiredprivate BoardService boardService;@Testvoid save() {BoardRequestDto boardSaveDto = new BoardRequestDto();boardSaveDto.setTitle("제목입니다.");boardSaveDto.setContent("내용입니다.");boardSaveDto.setRegisterId("작성자");Long result = boardService.save(boardSaveDto);if (result > 0) {System.out.println("# Success save() ~");findAll();findById(result);} else {System.out.println("# Fail Save() ~");}}void findAll() {List < BoardResponseDto > list = boardService.findAll();if (list != null) {System.out.println("# Success findAll() : " + list.toString());} else {System.out.println("# Fail findAll() ~");}}void findById(Long id) {BoardResponseDto info = boardService.findById(id);if (info != null) {System.out.println("# Success findById() : " + info.toString());updateBoard(id);} else {System.out.println("# Fail findById() ~");}}void updateBoard(Long id) {BoardRequestDto boardRequestDto = new BoardRequestDto();boardRequestDto.setId(id);boardRequestDto.setTitle("업데이트 제목");boardRequestDto.setContent("업데이트 내용");boardRequestDto.setRegisterId("작성자");int result = boardService.updateBoard(boardRequestDto);if (result > 0) {System.out.println("# Success updateBoard() ~");} else {System.out.println("# Fail updateBoard() ~");}}}
테스트 코드 실행2가지 방법 중 선택하여 진행한다.실행 시 @Test 어노테이션이 있는 메서드가 실행된다.방법① 프로젝트 우 클릭 → Run As → JUnit Test방법② 소스파일에서 우 클릭 → Run As → JUnit Test
방법①방법②
JUnit 탭에서 진행 결과를 확인할 수 있다.
성공 로그가 출력되었고, 데이터베이스에서도 직접 확인해본다.
프로젝트 Import압축을 풀고, 이클립스 import → General → Existing Projects into Workspace
board_study.zip0.09MB
Spring Boot (게시판) - 1 | 스프링 부트 프로젝트 만들기
Spring Boot (게시판) - 2 | 데이터베이스(MariaDB) 연동 및 JPA CRUD
Spring Boot (게시판) - 3 | 등록, 상세, 리스트 페이지 구현하기
Spring Boot (게시판) - 4 | 수정, 삭제 구현하기